/* 
 * File:   Complex.cpp
 * Author: Gab'z
 * 
 * Created on 29 mai 2012, 12:26
 */

#include "Complex.h"
#include "../../../exception/ArithmeticException.h"
#include "Integer.h"
#include "Real.h"
#include "model/Context.h"
#include <complex>

namespace calculator {

    Complex::Complex(const Complex& c) : Number() {
        _re = c._re->clone();
        _im = c._im->clone();
    }

    Complex::Complex(const SimpleNumber& x) : Number() {
        _re = x.clone();
        _im = new Integer(0);
    }

    Complex::Complex(const SimpleNumber* x) : Number() {
        _re = x->clone();
        _im = new Integer(0);
    }

    Complex::Complex(const SimpleNumber& x1, const SimpleNumber& x2) : Number() {
        _re = x1.clone();
        _im = x2.clone();
    }

    Complex::Complex(const SimpleNumber* x1, const SimpleNumber* x2) : Number() {
        _re = x1->clone();
        _im = x2->clone();
    }

    Complex::~Complex() {
        delete _re;
        delete _im;
    }

    Complex* Complex::clone() const {
        return new Complex(_re, _im);
    }

    std::string Complex::isExecutable() const {
        if (Context::getInstance()->getParameters()->getComplexMode() == false) return "Complex mode OFF";
        return "";
    }

    std::string Complex::toString() const {
        std::ostringstream oss;
        oss << _re->toString();
        oss << "$";
        oss << _im->toString();
        return oss.str();
    }

    Complex& Complex::operator=(const Complex& c) {
        if (this != &c) {
            delete _re;
            delete _im;
            _re = c._re;
            _im = c._im;
        }
        return *this;
    }

    Complex& Complex::operator+=(const Complex& c) {
        SimpleNumber* c_re = add(_re, c._re);
        SimpleNumber* c_im = add(_im, c._im);
        delete _re;
        delete _im;
        _re = c_re;
        _im = c_im;
        return *this;
    }

    Complex& Complex::operator-=(const Complex& c) {
        SimpleNumber* c_re = sub(_re, c._re);
        SimpleNumber* c_im = sub(_im, c._im);
        delete _re;
        delete _im;
        _re = c_re;
        _im = c_im;
        return *this;
    }

    Complex& Complex::operator*=(const Complex& co) {
        SimpleNumber* a = mul(_re, co._re);
        SimpleNumber* b = mul(_im, co._im);
        SimpleNumber* c = mul(_re, co._im);
        SimpleNumber* d = mul(_im, co._re);
        SimpleNumber* _re_c = sub(a, b);
        SimpleNumber* _im_c = add(c, d);
        delete a;
        delete b;
        delete c;
        delete d;
        delete _re;
        delete _im;
        _re = _re_c;
        _im = _im_c;

        return *this;
    }

    Complex& Complex::operator/=(const Complex& co) {
        if (co._re->value() == 0 && co._im->value() == 0) throw ArithmeticException("Complex divided by 0");
        SimpleNumber* a = mul(_re, co._re);
        SimpleNumber* b = mul(_im, co._im);
        SimpleNumber* c = mul(co._re, co._re);
        SimpleNumber* d = mul(co._im, co._im);
        SimpleNumber* e = add(a, b);
        SimpleNumber* f = add(c, d);
        SimpleNumber* _re_c = div(e, f);

        SimpleNumber* g = mul(_im, co._re);
        SimpleNumber* h = mul(_re, co._im);
        SimpleNumber* i = mul(co._re, co._re);
        SimpleNumber* j = mul(co._im, co._im);
        SimpleNumber* k = sub(g, h);
        SimpleNumber* l = add(i, j);
        SimpleNumber* _im_c = div(k, l);
        delete a;
        delete b;
        delete c;
        delete d;
        delete e;
        delete f;
        delete g;
        delete h;
        delete i;
        delete j;
        delete k;
        delete l;
        delete _re;
        delete _im;
        _re = _re_c;
        _im = _im_c;

        return *this;
    }

    Complex& Complex::pow(const SimpleNumber& sn) {
        std::complex<double> z(_re->value(), _im->value());
        z = std::exp(sn.value() * std::log(z));

        delete _re;
        delete _im;
        _re = new Real(std::real(z));
        _im = new Real(std::imag(z));
        return *this;
    }

    Complex operator+(const Complex& c1, const Complex& c2) {
        Complex c(c1);
        c += c2;
        return c;
    }

    Complex operator-(const Complex& c1, const Complex& c2) {
        Complex c(c1);
        c -= c2;
        return c;
    }

    Complex operator*(const Complex& c1, const Complex& c2) {
        Complex c(c1);
        c *= c2;
        return c;
    }

    Complex operator/(const Complex& c1, const Complex& c2) {
        Complex c(c1);
        c /= c2;
        return c;
    }

    Complex pow(const Complex& c1, const SimpleNumber& sn) {
        Complex c(c1);
        c.pow(sn);
        return c;
    }
}